using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;

namespace NetCorePal.Extensions.DistributedTransactions.CAP.SourceGenerators
{
    [Generator(LanguageNames.CSharp)]
    public class CapIntegrationConvertDomainEventHandlerSourceGenerator : IIncrementalGenerator
    {
        public void Initialize(IncrementalGeneratorInitializationContext context)
        {
            // 1. 配置选项管道
            var configOptions = context.AnalyzerConfigOptionsProvider
                .Select(static (options, _) => 
                    options.GlobalOptions.TryGetValue("build_property.RootNamespace", out var ns) 
                        ? ns 
                        : null);

            // 2. 语义分析管道
            var handlerSymbols = 
                context.SyntaxProvider
                .CreateSyntaxProvider(
                    predicate: static (node, _) => 
                        node is TypeDeclarationSyntax { BaseList.Types.Count: > 0 },
                    transform: static (ctx, _) => GetTypeSymbol(ctx))
                .Where(static symbol => 
                    symbol?.AllInterfaces.Any(i => i.Name == "IIntegrationEventConverter") == true)
                .Combine(context.CompilationProvider)
                .Combine(configOptions)
                .WithTrackingName("IntegrationConverterPipeline");

            // 3. 注册生成逻辑
            context.RegisterSourceOutput(handlerSymbols,
                (ctx, tuple) => GenerateCode(
                    ctx,
                    tuple.Left.Left,  // INamedTypeSymbol
                    tuple.Left.Right, // Compilation
                    tuple.Right));    // RootNamespace
        }

        private static INamedTypeSymbol? GetTypeSymbol(GeneratorSyntaxContext context)
        {
            var typeDeclaration = (TypeDeclarationSyntax)context.Node;
            return context.SemanticModel.GetDeclaredSymbol(typeDeclaration) as INamedTypeSymbol;
        }

        private static void GenerateCode(
            SourceProductionContext context,
            INamedTypeSymbol? integrationConvertTypeSymbol,
            Compilation compilation,
            string? rootNamespace)
        {
            if (string.IsNullOrWhiteSpace(rootNamespace)) return;
            if (integrationConvertTypeSymbol is null) return;
            string className = integrationConvertTypeSymbol.Name;

            //根据dbContextType继承的接口IIntegrationEventHandle<TIntegrationEvent> 推断出TIntegrationEvent类型
            var convertNamespace = integrationConvertTypeSymbol.ContainingNamespace.ToString();
            var usingNamespace = integrationConvertTypeSymbol.ContainingNamespace.ContainingNamespace.ToString();

            var iinterface = integrationConvertTypeSymbol.AllInterfaces
                .FirstOrDefault(i => i.Name == "IIntegrationEventConverter");
            if (iinterface == null)
            {
                return;
            }

            var domainEvent = iinterface.TypeArguments[0].Name;
            var domainEventNameSpace = iinterface.TypeArguments[0].ContainingNamespace.ToString();
            string source = $@"// <auto-generated/>
using {convertNamespace};
using NetCorePal.Extensions.DistributedTransactions;
using NetCorePal.Extensions.Domain;

namespace  {usingNamespace}.DomainEventHandlers
{{
    /// <summary>
    /// {className}DomainEventHandlers
    /// </summary>
    public class {className}DomainEventHandler(IIntegrationEventPublisher integrationEventPublisher,
{className} converter) : IDomainEventHandler<global::{domainEventNameSpace}.{domainEvent}>
    {{
        /// <summary>
        /// {className}DomainEventHandler
        /// </summary>
        /// <param name=""notification"">notification</param>
        /// <param name=""cancellationToken"">cancellationToken</param>
        public Task Handle(global::{domainEventNameSpace}.{domainEvent} notification, CancellationToken cancellationToken){{
            // 转移操作发出集成事件
            var integrationEvent = converter.Convert(notification);
            return integrationEventPublisher.PublishAsync(integrationEvent, cancellationToken);
        }}
        
    }}
}}
";
            context.AddSource($"{className}DomainEventHandler.g.cs", SourceText.From(source, Encoding.UTF8));
        }
    }
}
